﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ABMath.ModelFramework.Data;
using MathNet.Numerics;
using MathNet.Numerics.LinearAlgebra;

namespace ABMath.ModelFramework.Visuals
{
    [Serializable]
    public class SpectralVisual : Visual
    {
        private TimeSeries ts;

        public Matrix Spectrum { get; protected set; }

        public override string GetDescription()
        {
            return "Computes spectrum of the differences of the inputs";
        }

        public override string GetShortDescription()
        {
            return "Spectrum";
        }

        private void Recompute()
        {
            // allocate space for results
            Spectrum = new Matrix(800, 2);

            // and decide on frequencies to evaluate at
            double f0 = 0.1;
            double fT = 48000;
            double logRange = Math.Log(fT) - Math.Log(f0);
            for (int i = 0; i < Spectrum.RowCount; ++i)
            {
                double tl = Math.Log(f0) + logRange*(i + 1)/Spectrum.RowCount;
                Spectrum[i, 0] = Math.Exp(tl);
            }

            if (ts.Count < 2)
                return;

            // then compute the Fourier transform as if the original TS is a step-function
            // since the data is not necessarily regularly-spaced, we have to use a brute-force
            // version of the computation instead of the FFT
            DateTime baseTime = ts.TimeStamp(0);
            for (int i=0 ; i<Spectrum.RowCount ; ++i)
            {
                double omega = 2*Math.PI*Spectrum[i, 0];
                var total = new Complex(0, 0);
                for (int t=1 ; t<ts.Count ; ++t)
                {
                    double delta = ts[t] - ts[t - 1];
                    double timePoint = (ts.TimeStamp(t) - baseTime).TotalDays;

                    // change it!
                    //delta = Math.Sin(49.5 * 2 * Math.PI * timePoint);

                    total += new Complex(0, -timePoint*omega).Exponential()*delta;
                }
                Spectrum[i, 1] = Math.Log(total.Modulus);
                Spectrum[i, 0] = Math.Log(Spectrum[i, 0]);
            }
        }

        protected override bool OnInputChanged()
        {
            ts = TheObject as TimeSeries;
            if (ts == null)
                return false;

            Recompute();

            if (AssociatedControl != null)
                AssociatedControl.DataHasChanged();
            return true;
        }

        public override List<Type> GetAllowedInputTypesFor(int socket)
        {
            if (socket != 0)
                throw new SocketException();
            return new List<Type> { typeof(TimeSeries) };
        }
    }
}
